/* Copyright 2008 Nozomi SATO. All Rights Reserved */

#include <stdio.h>

#include "BMPFile.h"

static u32 Getu32(const u32 *p) {
	u8 *u8p = (u8 *)p;
	return (u8p[0])|(u8p[1] << 8)|(u8p[2] << 16)|(u8p[3] << 24);
}

static u16 Getu16(const u16 *p) {
	u8 *u8p = (u8 *)p;
	return (u8p[0])|(u8p[1] << 8);
}

BMPFile::BMPFile()
{
	m_startOfImage = 0;
	m_currentLine = 0;
	m_fp = NULL;
}

BMPFile::~BMPFile()
{
	if (m_fp) {
		fclose(m_fp);
		m_fp = NULL;
	}	
}

#define ROUNDUP(x, y)	((((x) + (y - 1)) / y) * y)

bool BMPFile::Init(const char *filename)
{
	FILE *fp;

	fp = fopen(filename, "r");
	if (fp == NULL) {
		return false;
	}

	BMPINFOHEADER bmpInfoHeader;
	fread(&bmpInfoHeader, 1, sizeof(bmpInfoHeader), fp);

	m_imageInfo.w = Getu32(&bmpInfoHeader.imageWidth);
	m_imageInfo.h = Getu32(&bmpInfoHeader.imageHeight);
	m_imageInfo.bpp = Getu16(&bmpInfoHeader.bitsPerPixel);
	m_imageInfo.pitch = ROUNDUP(m_imageInfo.w * m_imageInfo.bpp / 8, 4);

	m_startOfImage = Getu32(&bmpInfoHeader.startOfImage);

	if (m_fp) {
		fclose(m_fp);	
	}
	m_currentLine = 0;
	m_fp = fp;

	return true;
}

bool BMPFile::GetImageInfo(ImageInfo &imageInfo) const
{
	imageInfo = m_imageInfo;
	return true;
}

bool BMPFile::ReadScanline(u8 *dst, u32 dstPitch, u32 numberOfLines)
{
	u32 bytesPerLine;
	switch (m_imageInfo.bpp) {
	case 24:
		bytesPerLine = ROUNDUP(m_imageInfo.w * 3, 4);
		break;
	default:
		return false;
	}

	while (numberOfLines-- && (m_currentLine < m_imageInfo.h)) {
		fseek(m_fp, m_startOfImage + bytesPerLine * (m_imageInfo.h - m_currentLine - 1), SEEK_SET);
		fread(dst, 1, m_imageInfo.w * 3, m_fp);

		m_currentLine++;
		dst += dstPitch;
	}
	return true;
}


//#define TEST

#ifdef TEST
int main()
{
	bool result;
	BMPFile bmpFile;
	result = bmpFile.Init("test.bmp");
	if (result != true) {
		return 1;
	}

	BMPFile::ImageInfo imageInfo;
	bmpFile.GetImageInfo(imageInfo);

	printf("w:%d h:%d bpp:%d\n", imageInfo.w, imageInfo.h, imageInfo.bpp);
}
#endif
